﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Animation;
using Microsoft.Phone.Controls;
using UIKit.Common;
using System.ComponentModel;
using System.Diagnostics;

namespace UIKit.Controls
{
    [TemplatePart(Name = ListScrollerName, Type = typeof(ScrollViewer))]
    [TemplatePart(Name = ContainerStackName, Type = typeof(StackPanel))]
    [TemplatePart(Name = HeaderContentName, Type = typeof(ContentPresenter))]
    [TemplatePart(Name = ItemsPresenterHostName,Type = typeof(Grid))]
    [TemplatePart(Name = ItemsPresenterName,Type = typeof(ItemsPresenter))]
    [TemplatePart(Name = TopFreshControlName,Type = typeof(TextBlock))]
    [TemplatePart(Name = BottomFreshControlName, Type = typeof(TextBlock))]
    public class WaterFallList : ItemsControl
    {
        #region ConstName
        private const string ListScrollerName = "ListScroller";
        private const string ContainerStackName = "ContainerStack";
        private const string HeaderContentName = "HeaderContent";
        private const string ListContaierName = "ListContaier";
        private const string ItemsPresenterName = "ItemsPresenter";
        private const string ItemsPresenterHostName = "ItemsPresenterHost";
        private const string TopFreshControlName = "TopFreshControl";
        private const string BottomFreshControlName = "BottomFreshControl";
        #endregion

        #region Fields
        private ScrollViewer _listScroller;
        private StackPanel _containerStack;
        private ContentPresenter _headerContent;
        private Grid _itemsPresenterHost;
        private ItemsPresenter _itemsPresenter;
        private Grid _itemsPanelGrid;
        private double[] _columnY;
        private GestureListener _gestureListener;//手势操作
        private TextBlock _topFreshControl;
        private TextBlock _bottomFreshControl;
        private double _oldVertical = 0.0;//上一次的偏移量
        private bool _layoutFlag = false;
        private List<KeyValueInfo> _allInfos;
        private List<KeyValueInfo> _infos;//所有信息的结合
        #endregion

        #region Event
        public event VerticalScrollChangedEventHandler VerticalScrollChanged;   //滚动更新事件
        public event SelectionChangedEventHandler SelectionChanged;             //选择变化事件
        public event TapItemChangedEventHandler TapItemChanged;                 //Tap不同选项事件
        public event EventHandler PullDownToFetch;                              //下拉加载新数据事件
        public event EventHandler PullUpToFetch;                                //上拉加载旧数据事件
        #endregion

        #region Constructor
        public WaterFallList()
        {
            this.DefaultStyleKey = typeof(WaterFallList);
            SelectedIndex = -1;
            this.VerticalScrollChanged += WaterFallVerticalScrollChanged;
        }
        #endregion

        #region DependencyProperties

        #region Header
        public object Header
        {
            get { return (object)GetValue(HeaderProperty); }
            set { SetValue(HeaderProperty, value); }
        }

        public static readonly DependencyProperty HeaderProperty =
            DependencyProperty.Register("Header", typeof(object), typeof(WaterFallList), new PropertyMetadata(OnHeaderChanged));

        private static void OnHeaderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            WaterFallList ctrl = (WaterFallList)d;
            ctrl.OnHeaderChanged(e.OldValue, e.NewValue);
        }
        protected virtual void OnHeaderChanged(object oldHeader, object newHeader)
        {
        }
        #endregion

        #region HeaderTemplate
        public DataTemplate HeaderTemplate
        {
            get { return (DataTemplate)GetValue(HeaderTemplateProperty); }
            set { SetValue(HeaderTemplateProperty, value); }
        }

        public static readonly DependencyProperty HeaderTemplateProperty =
            DependencyProperty.Register("HeaderTemplate", typeof(DataTemplate), typeof(WaterFallList), new PropertyMetadata(OnHeaderTemplateChanged));

        private static void OnHeaderTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            WaterFallList ctrl = (WaterFallList)d;
            ctrl.OnHeaderTemplateChanged((DataTemplate)e.OldValue, (DataTemplate)e.NewValue);
        }

        protected virtual void OnHeaderTemplateChanged(DataTemplate oldHeaderTemplate, DataTemplate newHeaderTemplate)
        {
        }
        #endregion

        #region SelectedIndex

        public int SelectedIndex
        {
            get { return (int)GetValue(SelectedIndexProperty); }
            set { SetValue(SelectedIndexProperty, value); }
        }

        public static readonly DependencyProperty SelectedIndexProperty =
            DependencyProperty.Register("SelectedIndex", typeof(int), typeof(WaterFallList), new PropertyMetadata(OnSelectedIndexChanged));

        private static void OnSelectedIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            WaterFallList ctrl = (WaterFallList)d;
            ctrl.OnSelectedIndexChanged(e.OldValue,e.NewValue);
        }

        protected virtual void OnSelectedIndexChanged(object oldValue, object newValue)
        {
            int oldIndex = (int)oldValue;
            int newIndex = (int)newValue;
            if (newIndex != -1)
            {
                SelectedIndex = newIndex;
                WaterFallItem container = (WaterFallItem)ItemContainerGenerator.ContainerFromIndex(newIndex);
                SelectedItem = ItemContainerGenerator.ItemFromContainer(container);

                IList removedItems = (null == oldValue) ? new object[0] : new object[] { oldValue };
                IList addedItems = (null == newValue) ? new object[0] : new object[] { newValue };
                if (this.SelectionChanged != null)
                {
                    this.SelectionChanged(this, new SelectionChangedEventArgs(removedItems, addedItems));
                }
            }
        }
        #endregion

        #region SelectedItem

        public object SelectedItem
        {
            get { return (object)GetValue(SelectedItemProperty); }
            set { SetValue(SelectedItemProperty, value); }
        }

        public static readonly DependencyProperty SelectedItemProperty =
            DependencyProperty.Register("SelectedItem", typeof(object), typeof(WaterFallList), new PropertyMetadata(OnSelectedItemChanged));

        protected static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            WaterFallList ctrl = (WaterFallList)d;
            ctrl.OnSelectedItemChanged(e.OldValue, e.NewValue);
        }

        protected virtual void OnSelectedItemChanged(object oldValue, object newValue)
        {
            if (newValue != oldValue)
            {
                if (newValue == null)
                {
                    SelectedIndex = -1;
                }
                else
                {
                    WaterFallItem container = (WaterFallItem)ItemContainerGenerator.ContainerFromItem(newValue);
                    int selectedIndex = ItemContainerGenerator.IndexFromContainer(container);
                    SelectedIndex = selectedIndex;
                    SelectedItem = ItemContainerGenerator.ItemFromContainer(container);
                }
            }
        }
        #endregion

        #region ColumnCount
        public int ColumnCount
        {
            get { return (int)GetValue(ColumnCountProperty); }
            set { SetValue(ColumnCountProperty, value); }
        }

        public static readonly DependencyProperty ColumnCountProperty =
            DependencyProperty.Register("ColumnCount", typeof(int), typeof(WaterFallList), new PropertyMetadata(OnColumnCountChanged));

        private static void OnColumnCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            WaterFallList ctrl = (WaterFallList)d;
            ctrl.OnColumnCountChanged((int)e.OldValue,(int) e.NewValue);
        }

        protected virtual void OnColumnCountChanged(int oldCloumnCount, int newColumnCount)
        {
        }
        #endregion

        #region ColumnMargin
        public double ColumnMargin
        {
            get { return (double)GetValue(ColumnMarginProperty); }
            set { SetValue(ColumnMarginProperty, value); }
        }

        public static readonly DependencyProperty ColumnMarginProperty =
            DependencyProperty.Register("ColumnMargin", typeof(double), typeof(WaterFallList), new PropertyMetadata(OnColumnMarginChanged));

        private static void OnColumnMarginChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            WaterFallList ctrl = (WaterFallList)d;
            ctrl.OnColumnMarginChanged((double)e.OldValue, (double)e.NewValue);
        }

        protected virtual void OnColumnMarginChanged(double oldMargin, double newMargin)
        {
            
        }
        #endregion

        #region DelModeTag
        public bool IsInDelMode
        {
            get { return (bool)GetValue(IsInDelModeProperty); }
            set { SetValue(IsInDelModeProperty, value); }
        }

        public static readonly DependencyProperty IsInDelModeProperty =
            DependencyProperty.Register("IsInDelMode", typeof(bool), typeof(WaterFallList), new PropertyMetadata(OnModeChanged));

        private static void OnModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            WaterFallList ctrl = (WaterFallList)d;
            ctrl.OnModeChanged((bool)e.OldValue, (bool)e.NewValue);
        }

        protected virtual void OnModeChanged(bool oldValue, bool newValue)
        {
            if (newValue)
            {

                _itemsPanelGrid.Children.Clear();
                foreach (var item in base.Items)
                {
                    WaterFallItem container = GetItemContainerForObject(item);
                    _itemsPanelGrid.Children.Add(container);
                }
                UpdateLayout();
            }
        }
        #endregion

        #region IsFlat
        public bool IsFlat
        {
            get { return (bool)GetValue(IsFlatProperty); }
            set { SetValue(IsFlatProperty, value); }
        }

        public static readonly DependencyProperty IsFlatProperty =
            DependencyProperty.Register("IsFlat", typeof(bool), typeof(WaterFallList), new PropertyMetadata(null));
        #endregion

        #endregion

        #region Override

        #region ApplyTemplate
        
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            //初始化控件
            _listScroller = (ScrollViewer)GetTemplateChild(ListScrollerName);
            _containerStack = (StackPanel)GetTemplateChild(ContainerStackName);
            _headerContent = (ContentPresenter)GetTemplateChild(HeaderContentName);
            _itemsPresenterHost = (Grid)GetTemplateChild(ItemsPresenterHostName);
            _itemsPresenter = (ItemsPresenter)GetTemplateChild(ItemsPresenterName);
            _topFreshControl = (TextBlock)GetTemplateChild(TopFreshControlName);
            _bottomFreshControl = (TextBlock)GetTemplateChild(BottomFreshControlName);
            _columnY = new double[ColumnCount];
            //注册事件
            if (DesignerProperties.IsInDesignTool && _itemsPresenter != null)
            {
                _itemsPresenter.LayoutUpdated += _itemsPresenter_LayoutUpdated;
            }
            if (_listScroller != null)
            {
                this.SetBinding(DependencyProperty.RegisterAttached("VerticalOffset", typeof(double), this.GetType(), new PropertyMetadata((d, e) =>
                {
                    if (VerticalScrollChanged != null)
                        VerticalScrollChanged(_listScroller, new VerticalScrollChangedEventArgs(_listScroller.VerticalOffset));
                })), new Binding("VerticalOffset") { Source = this._listScroller });
                _gestureListener = GestureService.GetGestureListener(_listScroller);
                _gestureListener.DragDelta += _gestureListener_DragDelta;
                _gestureListener.DragCompleted += _gestureListener_DragCompleted;
            }
        }
        #endregion

        #region DataItem-UIItem
        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return item is WaterFallItem;
        }

        protected override DependencyObject GetContainerForItemOverride()
        {
            return new WaterFallItem();
        }

        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            base.PrepareContainerForItemOverride(element, item);
            HideBottomFresh();
            if (!_layoutFlag  && !DesignerProperties.IsInDesignTool)
            {
                _infos = null;
                _itemsPresenter.LayoutUpdated += new EventHandler(_itemsPresenter_LayoutUpdated);
                _layoutFlag = true;
            }
            WaterFallItem container = (WaterFallItem)element;
            container.VerticalAlignment = VerticalAlignment.Top;
            container.HorizontalAlignment = HorizontalAlignment.Left;
            container.Tap += container_Tap;
            KeyValueInfo info = new KeyValueInfo(item, container);
            this.Infos.Add(info);
            this.AllInfos.Add(info);
        }

        protected override void ClearContainerForItemOverride(DependencyObject element, object item)
        {
            base.ClearContainerForItemOverride(element, item);
            if (_itemsPanelGrid.Children.Contains((UIElement)element))
            {
                _itemsPanelGrid.Children.Remove((UIElement)element);
            }
            KeyValueInfo removeInfo = AllInfos.Where(x => x.Key == item).FirstOrDefault();
            int removeIndex = AllInfos.IndexOf(removeInfo);
            _columnY = new double[ColumnCount];
            AllInfos.Remove(removeInfo);
            ResetElementPosition(removeIndex);
        }

        protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
        {
            base.OnItemsChanged(e);
        }
        #endregion
        
        #endregion

        #region Delegate
        void WaterFallVerticalScrollChanged(object sender, VerticalScrollChangedEventArgs e)
        {
            if (!IsInDelMode)
            {
                double verticalOffset = _listScroller.VerticalOffset;

                double monitorScreenHeight = _listScroller.ExtentHeight - verticalOffset;
                double screenHeight = _listScroller.ViewportHeight;
                //当动态区域小于可以区域高度的1.5倍并且是向下滑动时加载新的数据
                Debug.WriteLine("distance-->" + (monitorScreenHeight - screenHeight * 1.5));
                Debug.WriteLine("offset-->" + (_oldVertical - verticalOffset));
                if (monitorScreenHeight <= screenHeight * 1.5 && _oldVertical - verticalOffset < 0)
                {
                    Debug.WriteLine("发送--->");
                    ShowBottomFresh();
                }
                //当动态区域大于可见区域且滑动100(这个距离可以根据情况改变)像素时，再去重新布局控件
                if (monitorScreenHeight > screenHeight && Math.Abs(_oldVertical - verticalOffset) > 100)
                {
                    foreach (object item in base.Items)
                    {
                        WaterFallItem container = GetItemContainerForObject(item);
                        double y = container.Margin.Top;
                        double vy = y - verticalOffset;
                        double headerHeight = _headerContent.ActualHeight;//要计算上头部的高度
                        if (vy < -300 - headerHeight || vy > 800 - headerHeight)
                        {
                            _itemsPanelGrid.Children.Remove(container);
                        }
                        else
                        {
                            if (!_itemsPanelGrid.Children.Contains(container))
                            {
                                _itemsPanelGrid.Children.Add(container);
                            }
                        }
                    }
                }
                _oldVertical = verticalOffset;
                _itemsPanelGrid.Height = GetMaxHeight();//每次变化都要重置高度
            }
        }

        void _itemsPresenter_LayoutUpdated(object sender, EventArgs e)
        {
            _itemsPresenter.LayoutUpdated -= _itemsPresenter_LayoutUpdated;
            _layoutFlag = false;
            _itemsPanelGrid = (Grid)VisualTreeHelper.GetChild(_itemsPresenter, 0);
            SetElementPosition();
        }

        void _gestureListener_DragCompleted(object sender, DragCompletedGestureEventArgs e)
        {
            HideTopFresh();
        }

        void _gestureListener_DragDelta(object sender, DragDeltaGestureEventArgs e)
        {
            if (e.VerticalChange > 30 && _listScroller.VerticalOffset == 0.0)
            {
                this.ShowTopFresh();
            }
        }

        void container_Tap(object sender, System.Windows.Input.GestureEventArgs e)
        {
            WaterFallItem container = (WaterFallItem)sender;
            int infoIndex = AllInfos.IndexOf(AllInfos.Where(x => x.Value == container).FirstOrDefault());
            int index = ItemContainerGenerator.IndexFromContainer(container);
            object item = ItemContainerGenerator.ItemFromContainer(container);
            this.SelectedIndex = index;
            if (this.TapItemChanged != null)
            {
                //TODO 可以在此处加选中的动画
                this.TapItemChanged(container, new TapEventArgs(index, item));
            }
        }
        #endregion

        #region MestureLayoutMethod
        /// <summary>
        /// 从某个索引开始重设元素的位置
        /// </summary>
        /// <param name="index"></param>
        private void ResetElementPosition(int index)
        {
            for (int i = 0; i < base.Items.Count; i++)
            {
                WaterFallItem container = GetItemContainerForObject(Items.ElementAt(i));
                int lowestIndex = GetLowestIndex();
                double width = container.ActualWidth;
                double height = container.ActualHeight;
                double x = (ColumnMargin + width) * lowestIndex + ColumnMargin;
                double y = _columnY[lowestIndex] + ColumnMargin;
                _columnY[lowestIndex] = y + height;
                //string str = string.Format("OldX-->{0},width-->{1},NewX-->{2},OldY--{3},height-->{4},NewY-->{5}", container.Margin.Left, width, x, container.Margin.Top, height, y);
                //Debug.WriteLine(str);
                if (i >= index)
                {
                    Storyboard st = new Storyboard();
                    st.Children.Add(ThicknessAnimation.Create(container, ContentControl.MarginProperty, TimeSpan.FromMilliseconds(1000), container.Margin, new Thickness(x, y, 0, 0)));
                    st.Begin();
                    //container.Margin = new Thickness(x, y, 0, 0);
                }
            }
        }

        private void SetElementPosition()
        {
            foreach (KeyValueInfo info in Infos) 
            {
                object item = info.Key;
                WaterFallItem container = (WaterFallItem)info.Value;
                int lowestIndex = GetLowestIndex();
                double width = container.ActualWidth;
                double height = container.ActualHeight;
                double x = (ColumnMargin + width) * lowestIndex + ColumnMargin;
                double y = _columnY[lowestIndex] + ColumnMargin;
                _columnY[lowestIndex] = y + height;
                container.Margin = new Thickness(x, y, 0, 0);
            }
            //_itemsPanelGrid.Height = GetMaxHeight();
        }

        protected virtual int GetLowestIndex()
        {
            int index = 0;
            for (int i = 1; i < ColumnCount; i++)
            {
                if (_columnY[index] > _columnY[i])
                {
                    index = i;
                }
            }
            return index;
        }

        /// <summary>
        /// 最高的列的高度
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public double GetMaxHeight()
        {
            int index = 0;
            for (int i = 1; i < ColumnCount; i++)
            {
                if (_columnY[index] < _columnY[i])
                {
                    index = i;
                }
            }
            return _columnY[index];
        }

        private void ShowTopFresh()
        {
            if (_topFreshControl != null && _topFreshControl.Visibility == Visibility.Collapsed && IsFlat)
            {
                _topFreshControl.Visibility = Visibility.Visible;
            }
        }

        private void HideTopFresh()
        {
            if (_topFreshControl != null && _topFreshControl.Visibility == Visibility.Visible && IsFlat)
            {
                _topFreshControl.Visibility = Visibility.Collapsed;
                if (PullDownToFetch != null)
                {
                    this.PullDownToFetch(this, EventArgs.Empty);
                }
            }
        }

        private void ShowBottomFresh()
        {
            if (_bottomFreshControl != null && _bottomFreshControl.Visibility == Visibility.Collapsed && IsFlat)
            {
                _bottomFreshControl.Visibility = Visibility.Visible;
                if (this.PullUpToFetch != null)
                {
                    this.PullUpToFetch(this, EventArgs.Empty);
                }
            }
        }

        private void HideBottomFresh()
        {
            if (_bottomFreshControl != null && _bottomFreshControl.Visibility == Visibility.Visible && IsFlat)
            {
                _bottomFreshControl.Visibility = Visibility.Collapsed;
            }
        }

        private Storyboard CreateRemoveStoryBoard(WaterFallItem container, Thickness oldThickness, Thickness newThickess)
        {
            Storyboard storyboard = new Storyboard();
            Timeline line = ThicknessAnimation.Create(container, ContentControl.MarginProperty, TimeSpan.FromMilliseconds(1000), oldThickness, newThickess);
            storyboard.Children.Add(line);
            return storyboard;
        }
        #endregion

        #region ItemContainerMethod
        private List<KeyValueInfo> Infos
        {
            get
            {
                if (this._infos == null)
                {
                    this._infos = new List<KeyValueInfo>();
                }
                return _infos;
            }
        }

        private List<KeyValueInfo> AllInfos
        {
            get
            {
                if (_allInfos == null)
                {
                    _allInfos = new List<KeyValueInfo>();
                }
                return _allInfos;
            }
        }

        private WaterFallItem GetItemContainerForObject(object item)
        {
            return (WaterFallItem)AllInfos.Where(x => x.Key == item).FirstOrDefault().Value;
        }
        #endregion
    }
}
